#include <string.h>
#include <WinSock2.h>
#include <Ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
#include "Common.h"

int SERVER_LIST_SIZE = -1;
unsigned short serverports[MAXSERVERS];
unsigned short testports[MAXSERVERS];

void startUpNetwork() {
	WORD wVersionRequested;
	WSADATA wdata;
	int err;
	
	wVersionRequested = MAKEWORD(1, 1);
	err = WSAStartup(wVersionRequested, &wdata);
	if(err != 0) {
		printf("%d", err);
	}
}

void getList() {
	int i;
	FILE *fp;
	char filename[100];
	
	if (SERVER_LIST_SIZE > 0) return;

	fp = fopen(SERVER_LIST_FILE, "r");
	if (fp == NULL) {
		sprintf(filename, "../%s", SERVER_LIST_FILE);
		fp = fopen(filename, "r");
	}
	
	fscanf(fp, " %d", &SERVER_LIST_SIZE);
	for (i=0; i<SERVER_LIST_SIZE; ++i) {
		fscanf(fp, " %hu %hu %hu %hu %hu %hu %s", &serverList[i].id, &serverList[i].bakcupPort, &serverList[i].commPort,
			&serverList[i].serverPort, &serverList[i].testPort, &serverList[i].registerPort, serverList[i].addr);
		serverports[i] = serverList[i].serverPort;
		testports[i] = serverList[i].testPort;
	}
	
	fclose(fp);
}

bool testSocket(SOCKET sock) {
	char buf[MAXBUFFSIZE];
	int recvBytes;
	bool res = true;
	int timeLimit;// , t, l=sizeof(int);
	
	timeLimit = 1000;
	setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeLimit, sizeof(int));
	recvBytes = recv(sock, buf, 0, 0);
// 	printf("recvBytes = %d, %d\n", recvBytes, GetLastError());
	if (recvBytes == -1 && GetLastError() != 10060) res = false;
	timeLimit = 3600000;
	setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeLimit, sizeof(int));

// 	getsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&t, &l);
// 	printf("in testSocket recv time out: %d\n", t);


	return res;
}

SOCKET createListenSocket(unsigned short port) {
	int ret;
	SOCKET sock;
	sock = createListenSocket2(port, &ret);
	if (ret < 0) return INVALID_SOCKET;
	else return sock;
}

SOCKET createListenSocket2(unsigned short port, int *retV) {
	SOCKADDR_IN addrServer;
	SOCKET sockServer;
	
	int ret;
	
	sockServer = socket(AF_INET,SOCK_STREAM,0);
	addrServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	addrServer.sin_family=AF_INET;
	addrServer.sin_port=htons(port);
	
	ret = bind(sockServer,(SOCKADDR*)&addrServer,sizeof(SOCKADDR));
// 	printf("%d %d\n", ret, GetLastError());
	if (ret < 0 && retV != NULL) {
		*retV = ret;
		closesocket(sockServer);
		return INVALID_SOCKET;
	}
	ret = listen(sockServer,MAXCONN);
// 	printf("%d %d\n", ret, GetLastError());
	if (ret < 0 && retV != NULL) {
		*retV = ret;
		closesocket(sockServer);
		return INVALID_SOCKET;
	}
	
	if (retV != NULL) *retV = 0;
	return sockServer;
}

SOCKET createNewConnectSocket(char* ipaddr, unsigned short *ports) {
	SOCKET sockClient;
	SOCKADDR_IN addrSrv;
	int retV = -1;
	int i=-1, t1, t2;

	int recvBytes;
	char recvBuffer[MAXBUFFSIZE];
	
	sockClient=socket(AF_INET,SOCK_STREAM,0);
	
	addrSrv.sin_addr.S_un.S_addr=inet_addr(ipaddr);
	addrSrv.sin_family=AF_INET;
	
	while (retV != 0) {
		++i;
		i = i%MAXSERVERS;
		addrSrv.sin_port=htons(ports[i]);
		retV = connect(sockClient,(SOCKADDR *)&addrSrv,sizeof(SOCKADDR));
		if (retV < 0) {
// 			printf("create connect socket error: %d ... port: %d\n", GetLastError(), ports[i]);
		}else {
// 			printf("waiting confirm boss... port: %d\n", ports[i]);
			recvBytes = recv(sockClient, recvBuffer, MAXBUFFSIZE, 0);
			if (recvBytes <= 0) {
				retV = -1;
				closesocket(sockClient);
				sockClient=socket(AF_INET,SOCK_STREAM,0);
			}
			else {
				recvBuffer[recvBytes] = 0;
// 				printf("in create connect, recv: %s\n", recvBuffer);
				sscanf(recvBuffer, "%d", &t1);
				if (t1 == NO) {
					retV = -1;
					closesocket(sockClient);
					sockClient=socket(AF_INET,SOCK_STREAM,0);
					sscanf(recvBuffer, "%d %d", &t1, &t2);
					if (t2 != -1) i = t2-1;
				}
			}
		}
	}
	printf("CONNECT TO %s:%d\n", ipaddr, ports[i]);
	return sockClient;

}

SOCKET createServerConnectSocket(char *ipaddr) {
	SOCKET sockClient;

	if (SERVER_LIST_SIZE == -1) getList();
	sockClient = createNewConnectSocket(ipaddr, serverports);
// 	printf("CONNCECT SERVER success.\n");
	return sockClient;
}

SOCKET createTesterConnectSocket(char *ipaddr) {
	SOCKET sockClient;

	if (SERVER_LIST_SIZE == -1) getList();
	sockClient = createNewConnectSocket(ipaddr, testports);
// 	printf("CONNECT TESTER success.\n");
	return sockClient;
}

SOCKET createConnectSocket(unsigned short port, char* ipaddr) {
	SOCKET sockClient;
	SOCKADDR_IN addrSrv;
	int retV;

	sockClient=socket(AF_INET,SOCK_STREAM,0);
	
	addrSrv.sin_addr.S_un.S_addr=inet_addr(ipaddr);
	addrSrv.sin_family=AF_INET;
	addrSrv.sin_port=htons(port);
	
	retV = connect(sockClient,(SOCKADDR *)&addrSrv,sizeof(SOCKADDR));

	return (retV<0) ? INVALID_SOCKET : sockClient;
}

TSocketInfo* tSocketInfo(void) {
	TSocketInfo* socketInfo = (TSocketInfo*)malloc(sizeof(TSocketInfo));
	socketInfo->buf.len = MAXBUFFSIZE;
	socketInfo->buf.buf = socketInfo->internalBuffer;
	memset(socketInfo->internalBuffer, 0, MAXBUFFSIZE);
	memset(&socketInfo->overlapped, 0, sizeof(socketInfo->overlapped));
	socketInfo->flag = 0;
	socketInfo->recvBytes = 0;
	return socketInfo;
}

TUserInfo* tUserInfo(KEYTYPE id) {
	TUserInfo* info;
	info = (TUserInfo*)malloc(sizeof(TUserInfo));
	//info = &nodePool[emptylist[emptyIdx++]];
	info->id = id;
	info->father = NULL;
	info->left = NULL;
	info->right = NULL;
	info->param = NULL;
	return info;
}

bool isLeft(TUserInfo* fa, TUserInfo* node) {
	return (fa->left!=NULL && fa->left->id == node->id);
}
bool isRight(TUserInfo* fa, TUserInfo* node) {
	return (fa->right!=NULL && fa->right->id == node->id);
}
void deleteNode(TUserInfo* node) {
// 	--emptyIdx;
// 	emptylist[emptyIdx] = (node-nodePool);
	free(node);
}

void copyNode(TUserInfo* src, TUserInfo* tar) {
	tar->id = src->id;
}

void zig(TUserInfo* root) {
	TUserInfo* fafa = NULL;
	root->father->left = root->right;
	if (root->right != NULL) root->right->father = root->father;

	fafa = root->father->father;
	if (fafa != NULL) {
		if (isLeft(fafa, root->father))
			fafa->left = root;
		else
			fafa->right = root;
	}
	root->father->father = root;

	root->right = root->father;
	root->father = fafa;
}

void zag(TUserInfo* root) {
	TUserInfo* fafa = NULL;
	root->father->right = root->left;
	if (root->left != NULL) root->left->father = root->father;

	fafa = root->father->father;
	if (fafa != NULL) {
		if (isRight(fafa, root->father))
			fafa->right = root;
		else
			fafa->left = root;
	}
	root->father->father = root;

	root->left = root->father;
	root->father = fafa;
}

void splay(TUserInfo* root, TUserInfo** rootUser) {
	TUserInfo* fafa = NULL;
	while (true) {
		if (root->father == NULL)
			break;
		fafa = root->father->father;

		if (fafa == NULL) {
			if (isLeft(root->father, root))
				zig(root);
			else
				zag(root);
			continue;
		}

		if (isLeft(fafa, root->father)) {
			if (isLeft(root->father, root)) {
				zig(root->father);
				zig(root);
			}
			else {
				zag(root);
				zig(root);
			}
		}
		else {
			if (isRight(root->father, root)) {
				zag(root->father);
				zag(root);
			}
			else {
				zig(root);
				zag(root);
			}
		}
	}
	*rootUser = root;
}

bool addUser(TUserInfo* root, KEYTYPE id, TUserInfo** rootUser) {
	TUserInfo* newUser = NULL;
	if (*rootUser == NULL) {
		*rootUser = tUserInfo(id);
		return true;
	}
	newUser = tUserInfo(id);
	while (true) {
		if (id == root->id) {
			deleteNode(newUser);
			return false;
		}
		if (id < root->id) {
			if (root->left == NULL) {
				root->left = newUser;
				break;
			}
			else
				root = root->left;
		}
		else {
			if (root->right == NULL) {
				root->right = newUser;
				break;
			}
			else
				root = root->right;
		}
	}
	newUser->father = root;

	splay(newUser, rootUser);
	return true;
}

//delete one user, and find a sub node to instead this one
void deleteSingleSubtreeNode(TUserInfo* root, TUserInfo** rootUser) {
	if (root->left == NULL) {
		if (root->father == NULL)
			*rootUser = root->right;
		else {
			if (isLeft(root->father, root))
				root->father->left = root->right;
			else
				root->father->right = root->right;
		}
		if (root->right != NULL)
			root->right->father = root->father;
		deleteNode(root);
	}
	else if (root->right == NULL) {
		if (root->father == NULL)
			*rootUser = root->left;
		else {
			if (isRight(root->father, root))
				root->father->right = root->left;
			else
				root->father->left = root->left;
		}
		if (root->left != NULL)
			root->left->father = root->father;
		deleteNode(root);
	}
}

bool deleteUser(TUserInfo* root, KEYTYPE id, TUserInfo** rootUser) {
	TUserInfo* theMaxNode = NULL;
	while (true) {
		if (root == NULL) return false;
		if (root->id == id) {
			if (root->left==NULL || root->right == NULL) {
				TUserInfo* fa = root->father;
				deleteSingleSubtreeNode(root, rootUser);
				if (fa != NULL)
					splay(fa, rootUser);
				break;
			}
			theMaxNode = root->left;
			while (theMaxNode->right != NULL) theMaxNode = theMaxNode->right;
			copyNode(theMaxNode, root);
			deleteSingleSubtreeNode(theMaxNode, rootUser);
			splay(root, rootUser);
			break;
		}
		if (id < root->id) root = root->left;
		else root = root->right;
	}
	return true;
}

bool find(TUserInfo* root, KEYTYPE id, TUserInfo** rootUser) {
	while (true) {
		if (root == NULL) return false;
		if (root->id == id) {
			splay(root, rootUser);
			return true;
		}
		if (id < root->id) root = root->left;
		else root = root->right;
	}
}

void getAllUsers(TUserInfo* root, KEYTYPE *retArray) {
	int idx = 0;
	TUserInfo **myStack;
	int stackPtr = 0;
	bool poped = false;

	if (root == NULL) return;
	myStack = (TUserInfo **)malloc(STACKSIZE*sizeof(TUserInfo*));

	while (true) {
// 		printf("root val: %d\n", root->id);
		if (!poped && root->left != NULL) {
			myStack[stackPtr++] = root;
			root = root->left;
			poped = false;
			continue;
		}
		
		retArray[idx++] = root->id;
		
		if (root->right != NULL){
			root = root->right;
			poped = false;
			continue;
		}

		if (stackPtr == 0) break;
		root = myStack[--stackPtr];
		poped = true;
	}

	free(myStack);
}
